package com.lzh.db.initializer;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author zhehen.lu
 * @date 2025/8/15 11:04
 */
public abstract class MybatisPlusEntityFillReplaceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    public MybatisPlusEntityFillReplaceInitializer() {
    }

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        try {
            this.scanAndReplace(this.getScanPackage());
        } catch (Throwable throwable) {
            try {
                throw throwable;
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public void scanAndReplace(String packageName) throws IOException, ClassNotFoundException {
        String packageSearchPath = "classpath*:" + resolveBasePackage(packageName) + '/' + "**/*.class";
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = pathMatchingResourcePatternResolver.getResources(packageSearchPath);
        List<String> replaceFillFieldList = this.getReplaceFillField();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(pathMatchingResourcePatternResolver.getResourceLoader());
        Resource[] resources1 = resources;
        int length = resources.length;

        for(int i = 0; i < length; ++i) {
            Resource resource = resources1[i];
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                String className = metadataReader.getClassMetadata().getClassName();
                Class<?> aClass = Class.forName(className);
                Field[] declaredFields = aClass.getDeclaredFields();
                Arrays.stream(declaredFields).filter((v) -> {
                    return v.isAnnotationPresent(TableField.class);
                }).filter((v) -> {
                    TableField annotation = (TableField)v.getAnnotation(TableField.class);
                    return replaceFillFieldList.contains(annotation.value());
                }).forEach((v) -> {
                    TableField tableField = (TableField)v.getAnnotation(TableField.class);
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(tableField);

                    try {
                        Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
                        memberValues.setAccessible(true);
                        Map memberValueMap = (Map)memberValues.get(invocationHandler);
                        memberValueMap.put("fill", FieldFill.DEFAULT);
                    } catch (IllegalAccessException | NoSuchFieldException exception) {
                        exception.printStackTrace();
                    }

                });
            }
        }

    }

    protected abstract String getScanPackage();

    protected abstract List<String> getReplaceFillField();

    private static String resolveBasePackage(String packageName) {
        return packageName.replaceAll("\\.", "/");
    }
}